package com.lx.entity;

import com.lx.annotation.Note;
import com.lx.util.LX;

import java.util.*;
import java.util.function.Supplier;

@Note("固定过期时间的缓存")
public class ExpireCache<T,R> {
    @Note("缓存key最大数量")
    private final int maxCapacity;
    @Note("缓存过期时长")
    private final int expire;
    @Note("缓存数据使用 数据大于maxCapacity个时 会移除快失效的数据")
    private final Map<T,ExpireKey> propertyMap = Collections.synchronizedMap(new LinkedHashMap<T,ExpireKey>() {
        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            //数据大于maxCapacity个时 为true时使用LRU算法移除数据[例: new LinkedHashMap<T,ExpireKey>(100, .75F, true) {...}] 为false时移除最久存储的数据
            return size() > maxCapacity;
        }
    });
    @Note("默认最大缓存数量")
    private static final int DEFAULT_CAPACITY = 5000;
    @Note("默认过期时长")
    private static final int DEFAULT_EXPIRE = 5000;

    @Note("默认时长5000毫秒 默认最大缓存数量 5000")
    public ExpireCache(){
        this.maxCapacity = DEFAULT_CAPACITY;
        this.expire = DEFAULT_EXPIRE;
    }
    public ExpireCache(@Note("缓存最大数量") int maxCapacity,@Note("缓存时长") int expire){
        if (maxCapacity < 1 || expire < 1) throw new IllegalArgumentException();
        this.maxCapacity = maxCapacity;
        this.expire = expire;
    }
    @Note("存入缓存")
    public void put(T key, R value){
        if (key == null) return;
        propertyMap.put(key, new ExpireKey(expire, value));
    }
    @Note("存入缓存")
    public void putAll(Map<T,R> map){
        if (map == null) return;
        for (Map.Entry<T, R> entry : map.entrySet()) {
            propertyMap.put(entry.getKey(), new ExpireKey(expire, entry.getValue()));
        }
    }


    @Note("获取缓存数据")
    public R get(T key){
        if (key == null) return null;
        ExpireKey expireKey = propertyMap.get(key);
        if (expireKey == null){
            return null;
        }
        if (expireKey.isExpire()){
            propertyMap.remove(key);
            return null;
        }
        return expireKey.value;
    }

    @Note("获取缓存中的数据 为空使用r, 并存入缓存")
    public R get(T key, R r){
        // 不能直接复用get(key) 会导致值为null时 缓存失效
        if (key == null) return null;
        ExpireKey expireKey = propertyMap.get(key);
        if (expireKey == null || expireKey.isExpire()){
            put(key ,r);
            return r;
        }
        return expireKey.value;
    }
    @Note("获取缓存中的数据 为空时执行supplier获取, 并存入缓存")
    public R get(T key, Supplier<R> supplier){
        if (key == null) return null;
        ExpireKey expireKey = propertyMap.get(key);
        if (expireKey == null || expireKey.isExpire()){
            R r = supplier == null ?null : supplier.get();
            put(key ,r);
            return r;
        }
        return expireKey.value;
    }

    @Note("缓存个数")
    public long size(){
        return propertyMap.size();
    }
    @Note("删除缓存")
    public void remove(T key){
        if (key == null) return;
        propertyMap.remove(key);
    }
    @Note("清理缓存")
    public void clear(){
        propertyMap.clear();
    }

    private class ExpireKey{
        long expire;
        R value;
        public ExpireKey(long delayTime, R value){
            this.expire = System.currentTimeMillis()+delayTime;
            this.value = value;
        }
        boolean isExpire(){
            return this.expire < System.currentTimeMillis();
        }

        @Override
        public String toString() {
            return "{" +
                    "expire=" +expire+
                    ",isExpire="+isExpire() +
                    ", value=" + value +
                    '}';
        }
    }

    @Override
    public String toString() {
        return propertyMap.toString();
    }
}
